package com.amap.poi.rpc;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.amap.poi.model.dto.PoiDataDTO;
import com.amap.poi.model.req.AmapPlaceTextReq;
import com.amap.poi.model.resp.AmapPoiDataResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 高德地图poi服务
 * 开发文档 - https://lbs.amap.com/api/webservice/guide/api/search/#text
 */
@Component
@Slf4j
public class AmapCallers {

    /**
     * 空数组 - 字符串形式
     */
    private static final String EMPTY_ARRAY = "[]";

    @Value(value = "${Amap.restApi}")
    private String restApi;
    @Value(value = "${Amap.poi.textApi}")
    private String textApi;
    @Value(value = "${Amap.key}")
    private String key;
    @Value(value = "${Amap.poi.offset}")
    private String offset;
    @Value(value = "${Amap.poi.cityLimit}")
    private String cityLimit;
    @Value(value = "${Amap.poi.extensions}")
    private String extensions;

    @Autowired
    private RestTemplate restTemplate;


    /**
     * 关键字搜索API - 获取原始数据
     *
     * @param keyword 关键词
     * @param city    城市
     * @return 集合
     */
    public List<PoiDataDTO> initialData(String keyword, String city) {
        log.info("【高德地图POI服务】｜ 关键字搜索API ｜ 获取原始数据-请求参数 ｜ 关键词={}, 城市={}", keyword, city);
        // 定义请求API的URL
        String apiUrl = restApi + textApi;
        // 一页一条数据获取总数量，后续用于计算
        Map<String, Object> totalCountMap = buildMapReq(keyword, city, "1", "1");
        AmapPoiDataResp amapPoiDataResp = getAmapPoiData(totalCountMap, apiUrl);
        // 判断总数量
        if ("0".equals(amapPoiDataResp.getCount())) {
            log.error("【高德地图POI服务】｜ 关键字搜索API ｜ 获取原始数据 ｜ API返参的列表总数为0");
            return new ArrayList<>();
        }
        // 计算出总页数
        int page = getPage(amapPoiDataResp.getCount());
        // 定义集合进行入参封装
        List<Map<String, Object>> reqMaps = new ArrayList<>();
        // 判断页数是否大于1，否则要遍历多页数据
        if (page > 1) {
            // 遍历组装
            for (int i = 1; i <= page; i++) {
                Map<String, Object> map = buildMapReq(keyword, city, String.valueOf(i), offset);
                reqMaps.add(map);
            }
        } else {
            Map<String, Object> map = buildMapReq(keyword, city, Integer.toString(page), offset);
            reqMaps.add(map);
        }
        // 判空
        if (CollectionUtils.isEmpty(reqMaps)) {
            return new ArrayList<>();
        }

        // 定义集合
        List<PoiDataDTO> results = new CopyOnWriteArrayList<>();
        // 请求API
        reqMaps.forEach(map -> {
            AmapPoiDataResp model = getAmapPoiData(map, apiUrl);
            if (!"0".equals(model.getCount())) {
                results.addAll(model.getPois());
            }
        });

        return results;
    }

    /**
     * 计算总页数
     *
     * @param totalCount
     * @return
     */
    private int getPage(String totalCount) {
        // 转换数量类型
        int count = Integer.parseInt(totalCount);
        int offSet = Integer.parseInt(offset);
        // 进行计算
        BigDecimal dividend = BigDecimal.valueOf(count);
        BigDecimal divisor = BigDecimal.valueOf(offSet);
        BigDecimal divide = dividend.divide(divisor, 2, RoundingMode.UP);
        // 向上取整
        return (int) Math.ceil(divide.doubleValue());
    }

    /**
     * 获取响应对象
     *
     * @param map    请求参数
     * @param apiUrl 请求API接口URL
     * @return
     */
    private AmapPoiDataResp getAmapPoiData(Map<String, Object> map, String apiUrl) {
        // 拼接url字符串
        String url = appendUrl(apiUrl, map);
        log.info("【高德地图POI服务】｜ 拼接API服务地址={}", url);
        // 发起请求
        ResponseEntity<String> forEntity;
        try {
            forEntity = restTemplate.getForEntity(url, String.class);
        } catch (RestClientException e) {
            log.error("【高德地图POI服务】｜ 发起请求出现异常，异常信息=", e);
            return new AmapPoiDataResp();
        }
        // 获取responseBody
        String responseBody = forEntity.getBody();
        log.info("【高德地图POI服务】｜ 获取responseBody={}", JSONUtil.toJsonStr(responseBody));
        // 判空
        if (StringUtils.isBlank(responseBody)) {
            log.error("【高德地图POI服务】｜ 获取responseBody为空");
            return new AmapPoiDataResp();
        }
        // 转化bean
        AmapPoiDataResp model = JSONUtil.toBean(responseBody, AmapPoiDataResp.class);
        // 返参的status值 【0-请求失败 1-请求成功】
        if (!"1".equals(model.getStatus())) {
            log.error(
                    "【高德地图POI服务】| API接口结果状态值不为1,详情结果状态值={},返回状态说明={}",
                    model.getStatus(), model.getInfo()
            );
            return new AmapPoiDataResp();
        }
        return model;
    }

    /**
     * 组装map
     *
     * @param keyword  关键词
     * @param city     城市...
     * @param pageNum  页码
     * @param pageSize 页数
     * @return
     */
    public Map<String, Object> buildMapReq(String keyword, String city, String pageNum, String pageSize) {
        // 定义对象并赋值
        AmapPlaceTextReq model = new AmapPlaceTextReq();
        model.setKey(key);
        model.setCitylimit(cityLimit);
        model.setExtensions(extensions);
        model.setKeywords(keyword);
        model.setCity(city);
        model.setPage(pageNum);
        model.setOffset(pageSize);
        // 转化为map
        return BeanUtil.beanToMap(model, false, true);
    }

    /**
     * 在指定url后追加参数
     *
     * @param url  请求的url
     * @param data 参数集合 key = value
     * @return 完整url地址
     */
    public String appendUrl(String url, Map<String, Object> data) {
        String newUrl = url;
        StringBuilder param = new StringBuilder();
        for (String key : data.keySet()) {
            param.append(key).append("=").append(data.get(key).toString()).append("&");
        }
        String paramStr = param.toString();
        paramStr = paramStr.substring(0, paramStr.length() - 1);
        if (newUrl.contains("?")) {
            newUrl += "&" + paramStr;
        } else {
            newUrl += "?" + paramStr;
        }
        return newUrl;
    }

}
